﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using Thrift;

namespace SearchInterface.helper
{
    class ThriftServerThread
    {
        private const int PORTTRIES=50;
        private Thrift.Server.TThreadPoolServer server;
        private TcpListener socket;
        private hydrap2p.ServiceLocation location;
        private String name;
        private Thread thread;

        /// <summary>
        /// Creates and binds to a random port and doesn't register a name with the Manager
        /// </summary>
        /// <exception cref="System.Net.Sockets.SocketException">Could not bind to a port within a reasonable time</exception>
        /// <exception cref="System.Net.Sockets.SocketException">May also mean that the Manager could not be found</exception>
        /// <exception cref="hydrap2p.manager.NameTaken">The given name was already registered</exception>
        /// <param name="serverType">The Thrift server to implement</param>
        public ThriftServerThread(Thrift.TProcessor serverType)
            : this(serverType, null, -1)
        {
        }        /// <summary>
        /// Creates and binds to a random port and registers a name with the Manager
        /// </summary>
        /// <exception cref="System.Net.Sockets.SocketException">Could not bind to a port within a reasonable time</exception>
        /// <exception cref="System.Net.Sockets.SocketException">May also mean that the Manager could not be found</exception>
        /// <exception cref="hydrap2p.manager.NameTaken">The given name was already registered</exception>
        /// <param name="serverType">The Thrift server to implement</param>
        /// <param name="name">The name to register in the Manager</param>
        public ThriftServerThread(Thrift.TProcessor serverType, String name) : this(serverType, name, -1)
        {    
        }
        /// <summary>
        /// Creates and binds to a port, preferably the given one or a random one
        /// </summary>
        /// <exception cref="System.Net.Sockets.SocketException">Could not bind to a port within a reasonable time</exception>
        /// <exception cref="System.Net.Sockets.SocketException">May also mean that the Manager could not be found</exception>
        /// <exception cref="hydrap2p.manager.NameTaken">The given name was already registered</exception>
        /// <param name="serverType">The Thrift server to implement</param>
        /// <param name="name">The name to register in the Manager</param>
        /// <param name="port">The default port to use</param>
        public ThriftServerThread(Thrift.TProcessor serverType, String name, int port)
        {
            Random ports = new Random();
            socket = null;
            int attempts=PORTTRIES;
            int minport=2000;
            int maxport=65000;
            this.name = name;

            if (port == -1)
                port = ports.Next(minport, maxport);

            while (socket==null && attempts>0)
            {
                try
                {
                    socket = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port);
                    socket.Start(); 
                }
                catch (SocketException)
                {
                    attempts--;
                    // this port is taken
                    port = ports.Next(minport, maxport);
                }
            }

            if (attempts == 0)
                throw new SocketException();
            
            // start the Thrift server
            Thrift.Transport.TServerTransport tsocket=new Thrift.Transport.TServerSocket(socket);
            server = new Thrift.Server.TThreadPoolServer(serverType, tsocket);

            location = new hydrap2p.ServiceLocation()
            {
                Address = "localhost",
                Port = port
            };
            // tell the manager
            if (name != null)
            {
                try
                {
                    hydrap2p.servicemanager.ServiceManager.Client manager = helper.ServiceConnector.GetServiceManager();
                    if (manager == null)
                        throw new Exception();

                    location = new hydrap2p.ServiceLocation()
                        {
                            Address = "localhost",
                            Port = port
                        };
                    manager.setServiceLocation(name, location);
                }
                catch (hydrap2p.servicemanager.NameTaken e)
                {
                    throw e;
                }
                catch (Exception)
                {
                    throw new System.Net.Sockets.SocketException();
                }
            }
        }

        public hydrap2p.ServiceLocation GetLocation()
        {
            return new hydrap2p.ServiceLocation()
            {
                Address = location.Address,
                Port = location.Port
            };
        }

        public void Start()
        {
            thread = new Thread(server.Serve);
            thread.Start();
        }

        public void Shutdown()
        {
            try
            {
                hydrap2p.servicemanager.ServiceManager.Client manager = helper.ServiceConnector.GetServiceManager();
                if (manager == null || name == null)
                    throw new Exception();
                manager.delServiceLocation(name);
            }
            catch (Exception)
            {
                // Failed to notify the manager that we're shutting down, oh noes
            }
            server.Stop();

        }
    }
}
